#include "lexic_units.h"
#include "main_container.h"
#include "errors_warnings.h"
#include "generic_table.cpp" // HACK

extern MainContainer *CompilerData;

LexicUnits::LexicUnits() {
	uniformCharacters = new GenericTable<LexicUnit>();
	constants = new FastStringTable();
	identifiers = new FastStringTable();

	identifiers->Add("this");
	identifiers->Add("base");
	identifiers->Add("$root$");
	//constants = new GenericTable<LexicUnit>();
	//constantsString = new GenericTable<string>();
	//identifiers = new GenericTable<LexicUnit>();
	//identifiersString = new GenericTable<string>();
}

LexicUnits::~LexicUnits() {
	if(uniformCharacters != NULL)	{
		delete uniformCharacters;
		uniformCharacters = NULL;
	}
	if(constants != NULL)	{
		delete constants;
		constants = NULL;
	}
	if(identifiers != NULL)	{
		delete identifiers;
		identifiers = NULL;
	}
}

bool LexicUnits::AddUnit(LexicUnit lexicUnit, char * str){
	LexicUnit *currentLexicUnit = &lexicUnit;
	//*currentLexicUnit = lexicUnit;
	if (currentLexicUnit->type == LUTypeKeyword || 
		   currentLexicUnit->type == LUTypeOperator ||
		   currentLexicUnit->type == LUTypeSpecialCharacter || 
		   currentLexicUnit->type == LUTypeSpecialType)	{
		this->uniformCharacters->Add(currentLexicUnit);
	}	else if (currentLexicUnit->type == LUTypeConstantBool ||
	          	currentLexicUnit->type == LUTypeConstantFloat ||
		          currentLexicUnit->type == LUTypeConstantInteger ||
		          currentLexicUnit->type == LUTypeConstantString)	{
		int newID = this->constants->Add(str);
		LexicUnit * another = new LexicUnit;
		*another = *currentLexicUnit;
		another->subtype = newID;
		this->uniformCharacters->Add(another);
	}	else if (currentLexicUnit->type == LUTypeIdentifier)	{
		int newID = this->identifiers->Add(str);
		LexicUnit * another = new LexicUnit;
		*another = *currentLexicUnit;
		another->subtype = newID;
		this->uniformCharacters->Add(another);
	}
	return true;
}
	
bool LexicUnits::AddUnit(LexicUnit lexicUnit) {
	LexicUnit *currentLexicUnit = new LexicUnit;
	*currentLexicUnit = lexicUnit;
	this->uniformCharacters->Add(currentLexicUnit);
	return true;
}

LexicUnit LexicUnits::GetLexicUnit(int index) {

#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("LexicUnits::GetLexicUnit");
#endif

	if(index < 0)	{
		// TODO - change this
		index = 0;
	}
	LexicUnit lu = *this->uniformCharacters->GetData(index);

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("LexicUnits::GetLexicUnit");
#endif

	return lu;
}


string LexicUnits::GetIdentifierString(int id) {
	LexicUnitWithString * luws = identifiers->GetUnit(id);
	if(luws != NULL)	return string(luws->name);
	else	return string("ICE_ERROR_IDENTIFIER_STRING");
}

string LexicUnits::GetLexicUnitString(LexicUnit * lu) {
	if (lu->type == LUTypeConstantBool ||
		   lu->type == LUTypeConstantFloat ||
		   lu->type == LUTypeConstantInteger ||
		   lu->type == LUTypeConstantString)	{
		// get in constants
		LexicUnitWithString * luws = constants->GetUnit(lu->subtype);
		if (luws != NULL)	return string(luws->name);
	}	else if (lu->type == LUTypeIdentifier)	{
		LexicUnitWithString * luws = identifiers->GetUnit(lu->subtype);
		if(luws != NULL)	return string(luws->name);
		// get in identifiers
	}	else if(lu->type == LUTypeKeyword)	return string(GetKeywordStringFromEnum((Keywords)lu->subtype));
	else if(lu->type == LUTypeOperator)	return string(GetOperatorStringFromEnum((Operators)lu->subtype));
	else if(lu->type == LUTypeSpecialCharacter)	return string(GetSpecialCharacterStringFromEnum((SpecialCharacters)lu->subtype));
	else if(lu->type == LUTypeSpecialType)	return string(GetSpecialTypeStringFromEnum((SpecialType)lu->subtype));
	// TODO - temporary - empty string
	return string("ICE_ERROR_STRING");
}

string LexicUnits::GetLexicUnitString(int lexUnitID) {
	if (lexUnitID < 0) return "PRODUCTION";
	return GetLexicUnitString(&GetLexicUnit(lexUnitID));
}

const char *LexicUnits::GetKeywordStringFromEnum(Keywords id) {
	switch(id)	{
		case KeywordUsing: return "using";
		case KeywordAs: return "as";
		case KeywordNamespace: return "namespace";
		case KeywordInt: return "int";
		case KeywordFloat: return "float";
		case KeywordBool: return "bool";
		case KeywordState: return "state";
		case KeywordTime: return "time";
		case KeywordVoid: return "void";
		case KeywordFacility: return "Facility";
		case KeywordStorage: return "Storage";
		case KeywordQueue: return "Queue";
		case KeywordADTable: return "ADTable";
		case KeywordGroup: return "Group";
		case KeywordOutputStream: return "OutputStream";
		case KeywordEnum: return "enum";
		case KeywordClass: return "class";
		case KeywordAbstract: return "abstract";
		case KeywordPublic: return "public";
		case KeywordPrivate: return "private";
		case KeywordProtected: return "protected";
		case KeywordTemplate: return "template";
		case KeywordConst: return "const";
		case KeywordStatic: return "static";
		case KeywordOverride: return "override";
		case KeywordVirtual: return "virtual";
		case KeywordFinal: return "final";
		case KeywordRead: return "read";
		case KeywordWrite: return "write";
		case KeywordStart: return "start";
		case KeywordProcess: return "process";
		case KeywordIf: return "if";
		case KeywordElse: return "else";
		case KeywordSwitch: return "switch";
		case KeywordCase: return "case";
		case KeywordDefault: return "default";
		case KeywordWhile: return "while";
		case KeywordDo: return "do";
		case KeywordFor: return "for";
		case KeywordBreak: return "break";
		case KeywordContinue: return "continue";
		case KeywordGoto: return "goto";
		case KeywordReturn: return "return";
		case KeywordWith: return "with";
		case KeywordStop: return "stop";
		case KeywordDerivation: return "derivation";
		case KeywordNumeric: return "numeric";
		case KeywordDiscrete: return "discrete";
		case KeywordContinuous: return "continuous";
		case KeywordINF: return "INF";
		case KeywordModel: return "model";
		case KeywordIn: return "in";
		case KeywordOut: return "out";
		case KeywordDefine: return "define";
		case KeywordGenerate: return "generate";
		case KeywordUpto: return "upto";
		case KeywordAfter: return "after";
		case KeywordEvery: return "every";
		case KeywordTerminate: return "terminate";
		case KeywordAdvance: return "advance";
		case KeywordEnter: return "enter";
		case KeywordUnits: return "units";
		case KeywordForming: return "forming";
		case KeywordLeave: return "leave";
		case KeywordFrom: return "from";
		case KeywordSeize: return "seize";
		case KeywordRelease: return "release";
		case KeywordInqueue: return "inqueue";
		case KeywordOutqueue: return "outqueue";
		case KeywordArrive: return "arrive";
		case KeywordDepart: return "depart";
		case KeywordWait: return "wait";
		case KeywordOn: return "on";
		case KeywordUntil: return "until";
		case KeywordThen: return "then";
		case KeywordTimeout: return "timeout";
		case KeywordJoin: return "join";
		case KeywordGroupLower: return "group";
		case KeywordCreate: return "create";
		case KeywordSend: return "send";
		case KeywordTo: return "to";
		case KeywordSplit: return "split";
		case KeywordInto: return "into";
		case KeywordGather: return "gather";
		case KeywordAnd: return "and";
		case KeywordDestroy: return "destroy";
		case KeywordExecute: return "execute";
		case KeywordExec: return "exec";
		case KeywordSimulation: return "simulation";
		case KeywordConfiguration: return "configuration";
		case KeywordRun: return "run";
		case KeywordSimulations: return "simulations";
		case KeywordClear: return "cleare";
		case KeywordReset: return "reset";
		case KeywordNull: return "null";
		case KeywordNew: return "new";
		case KeywordInstance: return "instance";
		case KeywordOf: return "of";
		case KeywordTrace: return "trace";
		default: return "UNKNOWN";
	}
}

const char *LexicUnits::GetOperatorStringFromEnum(Operators id) {
	switch(id)	{
		case OperatorAddition: return "+";
		case OperatorSubtraction: return "-";
		case OperatorNegation: return "-";
		case OperatorMultiplication: return "*";
		case OperatorDivision: return "/";
		case OperatorModulus: return "%";
		case OperatorExponentiation: return "^";
		case OperatorNot: return "!";
		case OperatorAssigment: return "=";
		case OperatorEqual: return "==";
		case OperatorNotEqual: return "!=";
		case OperatorLessThan: return "<";
		case OperatorGreaterThan: return ">";
		case OperatorLessThanOrEqualTo: return "<=";
		case OperatorGreaterThanEqualTo: return ">=";
		case OperatorAnd: return "&&";
		case OperatorOr: return "||";
		case OperatorXor: return "^^";
		default: return "UNKNOWN";
	}
}

const char * LexicUnits::GetSpecialCharacterStringFromEnum(SpecialCharacters id) {
	switch(id)	{
		case SpecialCharacterSemicolon: return ";";
		case SpecialCharacterLeftSquareBracket: return "[";
		case SpecialCharacterRightSquareBracket: return "]";
		case SpecialCharacterPlusOrMinus: return "+-";
		case SpecialCharacterLeftParenthesis: return "(";
		case SpecialCharacterRightParenthesis: return ")";
		case SpecialCharacterComma: return ",";
		case SpecialCharacterPeriod: return ".";
		case SpecialCharacterColon: return ":";
		case SpecialCharacterLeftCurlyBrace: return "{";
		case SpecialCharacterRightCurlyBrace: return "}";
		case SpecialCharacterTheArrowOperator: return "->";
		default: return "UNKNOWN";
	}
}

const char * LexicUnits::GetSpecialTypeStringFromEnum(SpecialType id) {
	switch(id)	{
		case endOfFile: return "END OF FILE";
		case endOfProject: return "END OF PROJECT";
		default: return "UNKNOWN";
	}
}